{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "d1f20875-c50d-46df-906b-49ec1d0c6002",
   "metadata": {},
   "source": [
    "# 8.3 通过自定义损失函数求解模型\n"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "c90a18b9-d8e8-431c-856c-35f1e4ed05e1",
   "metadata": {},
   "source": [
    "### 1.任务描述\n",
    "\n",
    "假设影响酸奶日销量的因素有$x_1$和$x_2$，利润为99，成本为1。\n",
    "\n",
    "要求：\n",
    "\n",
    "1. 自制销售数据集\n",
    "\n",
    "   自制32个销售数据，每个样本有2个特征（销量影响因素）和1个标签（销量）。其中，销量与销量影响因素$x_1$和$x_2$之间是线性关系：$y=x_1+x_2$\n",
    "2. 根据销售数据构建模型，预测酸奶日销量，从而指导生产"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "f5b4fc39-cbcf-432a-bf1e-e75e642d4b87",
   "metadata": {},
   "source": [
    "### 2.知识准备\n",
    "\n",
    "见教程。"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "c1d0295a-4ac4-470a-8263-027a3d69ac2c",
   "metadata": {},
   "source": [
    "### 3.任务分析\n",
    "\n",
    "若预测少了，损失的是利润；若预测多了，损失的是成本。如果目标是利润最大化，由于利润是99，成本是1，那么我们希望模型尽量往多了预测。\n",
    "\n",
    "可以设置自定义损失函数：\n",
    "\n",
    "$$f(\\hat y,y)=\\begin{cases}\r\n",
    "PROFIT(y-\\hat y) ,\\hat y<y\\\\\r\n",
    "COST(\\hat y ,y),\\hat y \\ge y\r\n",
    "\\end{cases}$$\n",
    "\n",
    "式中，y是标签，$\\hat y$是预测值，PROFIT是利润系数，COST是成本系数。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "435c6090-cfda-4f46-a550-22a368e41e4a",
   "metadata": {},
   "source": [
    "### 4.任务实施\n"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "ec75eb6c-5da3-467d-a471-ca3b47242dd6",
   "metadata": {},
   "source": [
    "执行代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "2ae9da58-e339-4d22-9f8d-ca255711d89e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "epoch: 2000 ,w: [[0.6568864]\n",
      " [0.9548757]]\n",
      "epoch: 4000 ,w: [[0.890576  ]\n",
      " [0.80038726]]\n",
      "epoch: 6000 ,w: [[0.91993624]\n",
      " [0.8974558 ]]\n",
      "epoch: 8000 ,w: [[0.53133124]\n",
      " [1.0363216 ]]\n",
      "epoch: 10000 ,w: [[0.8376113 ]\n",
      " [0.59827346]]\n"
     ]
    }
   ],
   "source": [
    "import tensorflow as tf\n",
    "import numpy as np\n",
    "# 1，自制数据集\n",
    "rdm = np.random.RandomState(seed=612)  \n",
    "# 特征（销量的影响因素）\n",
    "x = rdm.rand(32, 2)\n",
    "x = tf.cast(x, dtype=tf.float32)\n",
    "# 标签（销量）\n",
    "y = [[x1 + x2 + (rdm.rand() / 10.0 - 0.05)] for (x1, x2) in x]\n",
    "\n",
    "# 2，超参数\n",
    "w = tf.Variable(tf.random.normal((2, 1), stddev=1, seed=1))\n",
    "# 迭代次数\n",
    "epoch = 10000\n",
    "# 学习率\n",
    "lr = 0.002\n",
    "# 成本\n",
    "COST = 99\n",
    "# 利润\n",
    "PROFIT = 1\n",
    "\n",
    "for epoch in range(1,epoch+1):\n",
    "    with tf.GradientTape() as tape:\n",
    "        y_ = tf.matmul(x, w)\n",
    "        # 损失函数\n",
    "        loss = tf.reduce_sum(tf.where(tf.greater(y_, y), (y_-y) * COST, (y-y_) * PROFIT))\n",
    "    # 求导\n",
    "    grads = tape.gradient(loss, w)\n",
    "    # 更新参数\n",
    "    w.assign_sub(lr * grads)\n",
    "    # 打印\n",
    "    if epoch % 2000 == 0:\n",
    "        print(\"epoch:\",epoch,\",w:\",w.numpy())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e3b3e36e-8418-4caf-af51-d8ac80e2a321",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
